package com.favccxx.search.application;

import com.favccxx.search.domain.datasource.dataset.AbstractDataSet;
import com.favccxx.search.domain.datasource.dataset.AbstractDataSetRepo;
import com.favccxx.search.domain.datasource.dataset.DataSetField;
import com.favccxx.search.domain.datasource.dataset.JdbcDataSet;
import com.favccxx.search.domain.datasource.dataset.viewobject.DataSetDefinitionVo;
import com.favccxx.search.domain.datasource.dataset.viewobject.TableFieldVo;
import com.favccxx.search.domain.datasource.datasource.*;
import com.favccxx.search.infrastructure.exception.AppException;
import com.favccxx.search.infrastructure.exception.ExceptionCode;
import com.favccxx.search.infrastructure.util.jdbc.JdbcUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class DataSetService {

    @Autowired
    AbstractDataSourceRepo dataSourceRepo;
    @Autowired
    IDataSourceService dataSourceService;
    @Autowired
    AbstractDataSetRepo abstractDataSetRepo;

    /**
     * Save jdbc dataSource
     *
     * @param jdbcDataSet
     */
    public void saveJdbcDataSet(JdbcDataSet jdbcDataSet) {
        abstractDataSetRepo.save(jdbcDataSet);
    }

    /**
     * Save Jdbc data sets
     *
     * @param dataSourceId
     * @param datasetNames
     */
    public void saveJdbcDataSets(String dataSourceId, String datasetNames) {
        if (StringUtils.isBlank(dataSourceId) || StringUtils.isBlank(datasetNames)) {
            throw new AppException(ExceptionCode.DataSet.SAVE_DATASET_FAILED, "invalid params dataSourceId and datasetNames");
        }

        AbstractDataSource abstractDataSource = dataSourceRepo.findById(dataSourceId).get();
        if (abstractDataSource == null) {
            throw new AppException(ExceptionCode.DataSet.SAVE_DATASET_FAILED, "invalid dataSourceId -> " + dataSourceId);
        }

        List<AbstractDataSet> list = abstractDataSetRepo.findByDataSourceId(dataSourceId);

        try {
            JdbcDataSource jdbcDataSource = (JdbcDataSource) abstractDataSource;
            JdbcConnection jdbcConnection = jdbcDataSource.getJdbcConnection();
            Connection connection = JdbcUtil.init(jdbcConnection.getDriver(), jdbcConnection.getUrl(), jdbcConnection.getUsername(), jdbcConnection.getPassword());
            String[] dataTables = datasetNames.split(",");
            List<DataSetDefinitionVo> dataList = JdbcUtil.getTableDefinition(connection, jdbcConnection.getSchema(), dataTables);
            //Save Jdbc data set to mongodb
            saveJdbcDataSet(dataSourceId, dataList);
        } catch (Exception e) {
            log.error("DataSetService.saveJdbcDataSets failed, " + e.getMessage(), e);
            throw new AppException(ExceptionCode.DataSet.SAVE_DATASET_FAILED, "invalid params");
        }

    }

    /**
     * Save all data sets by data source id
     *
     * @param dataSourceId
     */
    public void saveAllDataSets(String dataSourceId) {
        if (StringUtils.isBlank(dataSourceId)) {
            throw new AppException(ExceptionCode.DataSet.SAVE_DATASET_FAILED, "invalid dataSourceId ->" + dataSourceId);
        }


        AbstractDataSource abstractDataSource = dataSourceRepo.findById(dataSourceId).get();
        if (abstractDataSource == null) {
            throw new AppException(ExceptionCode.DataSet.SAVE_DATASET_FAILED, "invalid dataSourceId -> " + dataSourceId);
        }

        List<AbstractDataSet> list = abstractDataSetRepo.findByDataSourceId(dataSourceId);
        abstractDataSetRepo.deleteAll(list);

        try {
            List<DataSetDefinitionVo> dataList = loadDataSetsByDataSourceId(dataSourceId);
            //Save Jdbc data set to mongodb
            saveJdbcDataSet(dataSourceId, dataList);
        } catch (Exception e) {
            log.error("DataSetService.saveJdbcDataSets failed, " + e.getMessage(), e);
            throw new AppException(ExceptionCode.DataSet.SAVE_DATASET_FAILED, "invalid params");
        }
    }


    /**
     * Find data set collection by data source id
     *
     * @param dataSourceId
     * @return
     */
    public List<DataSetDefinitionVo> loadDataSetsByDataSourceId(String dataSourceId) {
        List<DataSetDefinitionVo> dataList = new ArrayList<>();
        AbstractDataSource abstractDataSource = dataSourceRepo.findById(dataSourceId).get();
        if (abstractDataSource == null) {
            throw new AppException(ExceptionCode.DataSet.SAVE_DATASET_FAILED, "invalid dataSourceId -> " + dataSourceId);
        }

        if (abstractDataSource != null) {
            JdbcDataSource dataSource = (JdbcDataSource) abstractDataSource;
            JdbcConnection jdbcConnection = dataSource.getJdbcConnection();
            String url = jdbcConnection.getUrl();
            String username = jdbcConnection.getUsername();
            String password = jdbcConnection.getPassword();
            String schema = jdbcConnection.getSchema();

            Connection connection = JdbcUtil.init(jdbcConnection.getDriver(), url, username, password);
            dataList = JdbcUtil.getTableDefinition(connection, schema);
        }


        return dataList;
    }

    public List<DataSetDefinitionVo> loadDataSetsByDataSourceCode(String dataSourceCode) {
        List<DataSetDefinitionVo> dataList = new ArrayList<>();
        AbstractDataSource abstractDataSource = dataSourceRepo.findById(dataSourceCode).get();
        if (abstractDataSource == null) {
            throw new AppException(ExceptionCode.DataSet.SAVE_DATASET_FAILED, "invalid data source code -> " + dataSourceCode);
        }

        if (abstractDataSource != null) {
            JdbcDataSource dataSource = (JdbcDataSource) abstractDataSource;
            JdbcConnection jdbcConnection = dataSource.getJdbcConnection();
            String url = jdbcConnection.getUrl();
            String username = jdbcConnection.getUsername();
            String password = jdbcConnection.getPassword();
            String schema = jdbcConnection.getSchema();

            Connection connection = JdbcUtil.init(jdbcConnection.getDriver(), url, username, password);
            dataList = JdbcUtil.getTableDefinition(connection, schema);
        }


        return dataList;
    }


    private void saveJdbcDataSet(String dataSourceId, List<DataSetDefinitionVo> dataList) {
        dataList.stream().forEach(dataSetVo -> {
            JdbcDataSet jdbcDataSet = new JdbcDataSet();
            jdbcDataSet.setCode(dataSetVo.getTableName());
            jdbcDataSet.setName(dataSetVo.getTableName());
            jdbcDataSet.setType(dataSetVo.getTableType());
            jdbcDataSet.setDataSourceId(dataSourceId);

            List<DataSetField> list = new ArrayList<>();
            for (TableFieldVo tableFieldVo : dataSetVo.getTableFields()) {
                DataSetField field = new DataSetField();
                field.setFieldName(tableFieldVo.getFieldName());
                field.setFieldType(tableFieldVo.getFieldType());
                field.setFiledLength(tableFieldVo.getFiledLength());
                list.add(field);
            }
            jdbcDataSet.setDataFields(list);
            abstractDataSetRepo.save(jdbcDataSet);
        });
    }


}
